#include "bbh_pattern.h"

int  bbh_pattern_create(void**pptr)
{
    struct bbh_pattern_s *ptr = (struct bbh_pattern_s*)malloc(sizeof(struct bbh_pattern_s));
    memset(ptr, 0, sizeof(struct bbh_pattern_s));

    bbh_linkitem_init(&ptr->root);

    *pptr = ptr;

    return 0;
}

int  bbh_pattern_output(void*_, pattern_output_callback cb, void*cb_data)
{
    int                     r = 0;
    struct bbh_pattern_s     * pat = (struct bbh_pattern_s *)_;

    bbh_linkitem_t          *plink;
    struct pattern_item_s    * item;

    BBH_LINKITEM_FOR_BEGIN(&pat->root, plink)
        item = ADDRESS_OF(plink, struct pattern_item_s, link_item);

        r += cb(cb_data, item->spec, item->s, item->len);

        if (r < 0)
            break;
    BBH_LINKITEM_FOR_END
    //}

    return r;
}

#define FMT_NONE        0
#define FMT_START       1
#define FMT_END         2
#define FMT_ALIGN       3
#define FMT_PREFIX      4
#define FMT_MINWIDTH    5
#define FMT_DOT         6
#define FMT_MAXWIDTH    7
#define FMT_SPEC        8

int  bbh_pattern_parse(void*_, const char*format)
{
    int                     r;
    int                     sta;
    const char              * pos, * sp;//*sptr;//, *s;
    char                    ch;
    int                     error;
    struct bbh_pattern_s     * ptr;
    struct pattern_item_s   item, * item_ptr;

    char                    * out;
    size_t                  out_count;
    
    out_count   = strlen(format);
    out         = (char*)malloc(out_count * 2);
    memset(out, 0, out_count *2);

    out_count   = 0;

    ptr         = (struct bbh_pattern_s *)_;
    ptr->pattern= out;

    sta         = FMT_NONE;
    error       = 0;
    r           = 0;
    sp          = NULL;
    pos         = NULL;

    while(*format) {

        pos = format;
        memset(&item, 0, sizeof(item));

        if ('%' == *pos) {
            pos++;
            sta = FMT_START;

            //item.align = '+';
            while((ch=*pos)) {
                switch(ch) {
                case '-':
                case '+':
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '.':
                    if (FMT_START == sta) {
                        sta = FMT_SPEC;
                    }
                    break;
                case 'm':       // message
                    if (FMT_START == sta) {
                        item.spec[0] = ch;//'%';
                        item.spec[1] = 0;
                        if ('s' == *(pos+1)) {
                            item.spec[1] = 's';
                            pos++;
                        }
                        sta = FMT_END;
                    } else
                        error = -1;
                    break;
                case 'n':       // \r\n
                case 't':
                case 'T':
                case '%':
                    if (FMT_START == sta) {
                        item.spec[0] = ch;//'%';
                        sta = FMT_END;
                    } else
                        error = -1;
                    break;
                case 'd':       // datetime
                    if (FMT_START == sta) {
                        item.spec[0] = ch;
                        pos++;
                        if ('('==*pos) {
                            sp = ++pos;
                            while(*pos && ')'!=*pos) pos++;

                            if (!*pos) {
                                error = -1;
                                break;
                            }

                            item.s = out + out_count;
                            item.len = pos - sp;
                            memcpy(item.s, sp, item.len);
                            out_count += item.len;
                            out[out_count++] = '\0';
                            // 记录日期格式串
                            //pos++;
                            sta = FMT_END;
                        } else {
                            pos--;
                            sta = FMT_END;
                        }
                    } else {
                        error = -1;
                    }
                    break;
                case 'u':   // us 微秒,6位数字字符串,取自gettimeofday(2)
                    if (FMT_START == sta) {
                        item.spec[0] = ch;
                        if ('s' == *(pos+1)) {
                            item.spec[1] = 's';
                            pos++;
                            sta = FMT_END;
                        } else
                            error = -1;
                    } else {
                        error = -1;
                    }
                    break;
                // strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);
                case 'c':       // category
                case 'F':
                case 'f':
                case 'U':
                case 'V':
                case 'v':
                    item.spec[0] = ch;
                    // 记录格式串
                    item.s = out + out_count;
                    item.len = pos - format + 1;
                    memcpy(item.s, format, item.len);
                    item.s[item.len - 1] = 's';
                    out_count += item.len;
                    out[out_count++] = '\0';
                    sta = FMT_END;
                    break;
                case 'L':
                    item.spec[0] = ch;
                    // 记录格式串
                    item.s = out + out_count;
                    item.len = pos - format + 1;
                    memcpy(item.s, format, item.len);
                    item.s[item.len - 1] = 'd';
                    out_count += item.len;
                    out[out_count++] = '\0';
                    sta = FMT_END;
                    break;
                default:
                    error = -1; // 不可识别
                    break;
                }
                // 
                if (FMT_END==sta || error < 0) {
                    if (FMT_END==sta)
                        pos++;
                    break;
                }

                pos++;
            }
        } else {
            while(*pos && ('%' != *pos)) pos++;
            item.spec[0] = 0;
            item.len = pos - format;
            item.s = out + out_count;
            memcpy(item.s, format, item.len);
            out_count += item.len;
            out[out_count++] = '\0';

            sta = FMT_END;
        }

        if (FMT_END == sta) {
            // 记录子项
            item_ptr = (struct pattern_item_s*)malloc(sizeof(struct pattern_item_s));
            memcpy(item_ptr, &item, sizeof(item));
            bbh_linkitem_pushback(&ptr->root, &item_ptr->link_item);
        } else {
            r = -1;
        }

        if (r < 0)
            break;

        format = pos;
    }

    return r;
}

// 返回值表示有错误
static int _verify_item(struct pattern_item_s*fi)
{
    if (!fi->len)
        return 0;

    char buffer[256];
    int r = 0;
    time_t now = time(NULL);

    switch(fi->spec[0]) {
    //case 'c':       // category
    case 'V':
    case 'v':
        r = snprintf(fi->s, 0, fi->s, "");
        break;
    case 'd':
        r = (int)strftime(buffer, 256, fi->s, (const struct tm*)localtime(&now));
        r = (r ? -1 : 0);
        break;
    }

    return r;
}
int  bbh_pattern_verify(void*_)
{
    int err = 0;
    struct bbh_pattern_s *pat = (struct bbh_pattern_s *)_;

    struct pattern_item_s   * item;
    bbh_linkitem_t *plink;

    BBH_LINKITEM_FOR_BEGIN(&pat->root, plink)
        item = ADDRESS_OF(plink, struct pattern_item_s, link_item);
        err = _verify_item(item);
        if (err) {
            break;
        }
    BBH_LINKITEM_FOR_END

    return err;
}
void bbh_pattern_clear(void*_)
{
    struct bbh_pattern_s *pat = (struct bbh_pattern_s *)_;

    if (pat->pattern)   free(pat->pattern);

    struct pattern_item_s   * item;
    bbh_linkitem_t*plink;
    while((plink=bbh_linkitem_popfront(&pat->root))) {
        item = ADDRESS_OF(plink, struct pattern_item_s, link_item);
        free(item);
    }

    pat->pattern = NULL;
}
void bbh_pattern_free(void*_)
{
    if (_) {
        bbh_pattern_clear(_);
        free(_);
    }
}
